/**
 * Copyright 2018 jianggujin (www.jianggujin.com).
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.jianggujin.modulelink.mvc.util;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.util.BitSet;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.ServletContext;
import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import com.jianggujin.modulelink.mvc.JActionContext;
import com.jianggujin.modulelink.util.JAssert;
import com.jianggujin.modulelink.util.JStringUtils;

/**
 * Web工具
 * 
 * @author jianggujin
 *
 */
public class JWebUtils {

   public static final String INCLUDE_REQUEST_URI_ATTRIBUTE = "javax.servlet.include.request_uri";
   public static final String INCLUDE_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.include.context_path";
   public static final String INCLUDE_SERVLET_PATH_ATTRIBUTE = "javax.servlet.include.servlet_path";
   public static final String INCLUDE_PATH_INFO_ATTRIBUTE = "javax.servlet.include.path_info";
   public static final String INCLUDE_QUERY_STRING_ATTRIBUTE = "javax.servlet.include.query_string";

   public static final String FORWARD_REQUEST_URI_ATTRIBUTE = "javax.servlet.forward.request_uri";
   public static final String FORWARD_CONTEXT_PATH_ATTRIBUTE = "javax.servlet.forward.context_path";
   public static final String FORWARD_SERVLET_PATH_ATTRIBUTE = "javax.servlet.forward.servlet_path";
   public static final String FORWARD_PATH_INFO_ATTRIBUTE = "javax.servlet.forward.path_info";
   public static final String FORWARD_QUERY_STRING_ATTRIBUTE = "javax.servlet.forward.query_string";

   public static final String ERROR_STATUS_CODE_ATTRIBUTE = "javax.servlet.error.status_code";
   public static final String ERROR_EXCEPTION_TYPE_ATTRIBUTE = "javax.servlet.error.exception_type";
   public static final String ERROR_MESSAGE_ATTRIBUTE = "javax.servlet.error.message";
   public static final String ERROR_EXCEPTION_ATTRIBUTE = "javax.servlet.error.exception";
   public static final String ERROR_REQUEST_URI_ATTRIBUTE = "javax.servlet.error.request_uri";
   public static final String ERROR_SERVLET_NAME_ATTRIBUTE = "javax.servlet.error.servlet_name";

   /**
    * 字符集正则匹配模式
    */
   private static final Pattern charsetPattern = Pattern.compile("(?i)\\bcharset=\\s*(?:\"|')?([^\\s,;\"']*)");
   /**
    * 默认字符编码
    */
   public static String DEFAULT_CHARACTER_ENCODING = "UTF-8";

   public static final String TEMP_DIR_CONTEXT_ATTRIBUTE = "javax.servlet.context.tempdir";
   private static final BitSet domainValid = new BitSet(128);

   static {
      for (char c = '0'; c <= '9'; c++) {
         domainValid.set(c);
      }
      for (char c = 'a'; c <= 'z'; c++) {
         domainValid.set(c);
      }
      for (char c = 'A'; c <= 'Z'; c++) {
         domainValid.set(c);
      }
      domainValid.set('.');
      domainValid.set('-');
   }

   /**
    * 获得临时文件目录
    * 
    * @param servletContext
    * @return
    */
   public static File getTempDir(ServletContext servletContext) {
      JAssert.checkNotNull(servletContext, "ServletContext must not be null");
      return (File) servletContext.getAttribute(TEMP_DIR_CONTEXT_ATTRIBUTE);
   }

   /**
    * 获得实际路径
    * 
    * @param servletContext
    * @param path
    * @return
    * @throws FileNotFoundException
    */
   public static String getRealPath(ServletContext servletContext, String path) throws FileNotFoundException {
      JAssert.checkNotNull(servletContext, "ServletContext must not be null");
      if (!path.startsWith("/")) {
         path = "/" + path;
      }
      String realPath = servletContext.getRealPath(path);
      if (realPath == null) {
         throw new FileNotFoundException("ServletContext resource [" + path
               + "] cannot be resolved to absolute file path - " + "web application archive not expanded?");
      }
      return realPath;
   }

   /**
    * 获得{@link HttpServletRequest}属性
    * 
    * @param context
    * @param name
    * @return
    */
   public static Object getAttribute(JActionContext context, String name) {
      JAssert.checkNotNull(context, "context must not be null");
      return getAttribute(context.getRequest(), name);
   }

   /**
    * 获得{@link HttpServletRequest}属性
    * 
    * @param request
    * @param name
    * @return
    */
   public static Object getAttribute(HttpServletRequest request, String name) {
      JAssert.checkNotNull(request, "Request must not be null");
      return request.getAttribute(name);
   }

   /**
    * 获得{@link HttpServletRequest}属性，该属性不存在则报错
    * 
    * @param context
    * @param name
    * @return
    * @throws IllegalStateException
    */
   public static Object getRequiredAttribute(JActionContext context, String name) throws IllegalStateException {
      JAssert.checkNotNull(context, "context must not be null");
      return getRequiredAttribute(context.getRequest(), name);
   }

   /**
    * 获得{@link HttpServletRequest}属性，该属性不存在则报错
    * 
    * @param request
    * @param name
    * @return
    * @throws IllegalStateException
    */
   public static Object getRequiredAttribute(HttpServletRequest request, String name) throws IllegalStateException {
      Object attr = getAttribute(request, name);
      if (attr == null) {
         throw new IllegalStateException("No request attribute '" + name + "' found");
      }
      return attr;
   }

   /**
    * 设置{@link HttpServletRequest}属性
    * 
    * @param context
    * @param name
    * @param value
    */
   public static void setAttribute(JActionContext context, String name, Object value) {
      JAssert.checkNotNull(context, "context must not be null");
      setAttribute(context.getRequest(), name, value);
   }

   /**
    * 设置{@link HttpServletRequest}属性
    * 
    * @param request
    * @param name
    * @param value
    */
   public static void setAttribute(HttpServletRequest request, String name, Object value) {
      JAssert.checkNotNull(request, "Request must not be null");
      if (value != null) {
         request.setAttribute(name, value);
      } else {
         request.removeAttribute(name);
      }
   }

   /**
    * 获得或创建{@link HttpServletRequest}属性
    * 
    * @param context
    * @param name
    * @param clazz
    * @return
    * @throws IllegalArgumentException
    */
   public static Object getOrCreateAttribute(JActionContext context, String name, Class<?> clazz)
         throws IllegalArgumentException {
      JAssert.checkNotNull(context, "context must not be null");
      return getOrCreateAttribute(context.getRequest(), name, clazz);
   }

   /**
    * 获得或创建{@link HttpServletRequest}属性
    * 
    * @param request
    * @param name
    * @param clazz
    * @return
    * @throws IllegalArgumentException
    */
   public static Object getOrCreateAttribute(HttpServletRequest request, String name, Class<?> clazz)
         throws IllegalArgumentException {

      JAssert.checkNotNull(request, "request must not be null");
      Object requestObject = request.getAttribute(name);
      if (requestObject == null) {
         try {
            requestObject = clazz.newInstance();
         } catch (InstantiationException ex) {
            throw new IllegalArgumentException("Could not instantiate class [" + clazz.getName()
                  + "] for request attribute '" + name + "': " + ex.getMessage());
         } catch (IllegalAccessException ex) {
            throw new IllegalArgumentException("Could not access default constructor of class [" + clazz.getName()
                  + "] for request attribute '" + name + "': " + ex.getMessage());
         }
         request.setAttribute(name, requestObject);
      }
      return requestObject;
   }

   /**
    * 获得SessionId
    * 
    * @param request
    * @return
    */
   public static String getSessionId(HttpServletRequest request) {
      JAssert.checkNotNull(request, "Request must not be null");
      HttpSession session = request.getSession(false);
      return (session != null ? session.getId() : null);
   }

   /**
    * 获得{@link HttpSession}属性
    * 
    * @param context
    * @param name
    * @return
    */
   public static Object getSessionAttribute(JActionContext context, String name) {
      JAssert.checkNotNull(context, "context must not be null");
      return getSessionAttribute(context.getRequest(), name);
   }

   /**
    * 获得{@link HttpSession}属性
    * 
    * @param request
    * @param name
    * @return
    */
   public static Object getSessionAttribute(HttpServletRequest request, String name) {
      JAssert.checkNotNull(request, "Request must not be null");
      HttpSession session = request.getSession(false);
      return (session != null ? session.getAttribute(name) : null);
   }

   /**
    * 获得{@link HttpSession}属性，该属性不存在则报错
    * 
    * @param context
    * @param name
    * @return
    * @throws IllegalStateException
    */
   public static Object getRequiredSessionAttribute(JActionContext context, String name) throws IllegalStateException {
      JAssert.checkNotNull(context, "context must not be null");
      return getRequiredSessionAttribute(context.getRequest(), name);
   }

   /**
    * 获得{@link HttpSession}属性，该属性不存在则报错
    * 
    * @param request
    * @param name
    * @return
    * @throws IllegalStateException
    */
   public static Object getRequiredSessionAttribute(HttpServletRequest request, String name)
         throws IllegalStateException {
      Object attr = getSessionAttribute(request, name);
      if (attr == null) {
         throw new IllegalStateException("No session attribute '" + name + "' found");
      }
      return attr;
   }

   /**
    * 设置{@link HttpSession}属性
    * 
    * @param context
    * @param name
    * @param value
    */
   public static void setSessionAttribute(JActionContext context, String name, Object value) {
      JAssert.checkNotNull(context, "context must not be null");
      setSessionAttribute(context.getRequest(), name, value);
   }

   /**
    * 设置{@link HttpSession}属性
    * 
    * @param request
    * @param name
    * @param value
    */
   public static void setSessionAttribute(HttpServletRequest request, String name, Object value) {
      JAssert.checkNotNull(request, "Request must not be null");
      if (value != null) {
         request.getSession().setAttribute(name, value);
      } else {
         HttpSession session = request.getSession(false);
         if (session != null) {
            session.removeAttribute(name);
         }
      }
   }

   /**
    * 获得或创建{@link HttpSession}属性
    * 
    * @param context
    * @param name
    * @param clazz
    * @return
    * @throws IllegalArgumentException
    */
   public static Object getOrCreateSessionAttribute(JActionContext context, String name, Class<?> clazz)
         throws IllegalArgumentException {
      JAssert.checkNotNull(context, "context must not be null");
      return getOrCreateSessionAttribute(context.getRequest(), name, clazz);
   }

   /**
    * 获得或创建{@link HttpSession}属性
    * 
    * @param request
    * @param name
    * @param clazz
    * @return
    * @throws IllegalArgumentException
    */
   public static Object getOrCreateSessionAttribute(HttpServletRequest request, String name, Class<?> clazz)
         throws IllegalArgumentException {

      JAssert.checkNotNull(request, "request must not be null");
      HttpSession session = request.getSession();
      Object sessionObject = session.getAttribute(name);
      if (sessionObject == null) {
         try {
            sessionObject = clazz.newInstance();
         } catch (InstantiationException ex) {
            throw new IllegalArgumentException("Could not instantiate class [" + clazz.getName()
                  + "] for session attribute '" + name + "': " + ex.getMessage());
         } catch (IllegalAccessException ex) {
            throw new IllegalArgumentException("Could not access default constructor of class [" + clazz.getName()
                  + "] for session attribute '" + name + "': " + ex.getMessage());
         }
         session.setAttribute(name, sessionObject);
      }
      return sessionObject;
   }

   /**
    * 获得{@link ServletContext}属性
    * 
    * @param context
    * @param name
    * @return
    */
   public static Object getContextAttribute(JActionContext context, String name) {
      JAssert.checkNotNull(context, "context must not be null");
      return getContextAttribute(context.getRequest(), name);
   }

   /**
    * 获得{@link ServletContext}属性
    * 
    * @param request
    * @param name
    * @return
    */
   public static Object getContextAttribute(HttpServletRequest request, String name) {
      JAssert.checkNotNull(request, "Request must not be null");
      HttpSession session = request.getSession(false);
      return (session != null ? session.getServletContext().getAttribute(name) : null);
   }

   /**
    * 获得{@link ServletContext}属性，该属性不存在则报错
    * 
    * @param context
    * @param name
    * @return
    * @throws IllegalStateException
    */
   public static Object getRequiredContextAttribute(JActionContext context, String name) throws IllegalStateException {
      JAssert.checkNotNull(context, "context must not be null");
      return getRequiredContextAttribute(context.getRequest(), name);
   }

   /**
    * 获得{@link ServletContext}属性，该属性不存在则报错
    * 
    * @param request
    * @param name
    * @return
    * @throws IllegalStateException
    */
   public static Object getRequiredContextAttribute(HttpServletRequest request, String name)
         throws IllegalStateException {
      Object attr = getContextAttribute(request, name);
      if (attr == null) {
         throw new IllegalStateException("No Context attribute '" + name + "' found");
      }
      return attr;
   }

   /**
    * 设置{@link ServletContext}属性
    * 
    * @param context
    * @param name
    * @param value
    */
   public static void setContextAttribute(JActionContext context, String name, Object value) {
      JAssert.checkNotNull(context, "context must not be null");
      setContextAttribute(context.getRequest(), name, value);
   }

   /**
    * 设置{@link ServletContext}属性
    * 
    * @param request
    * @param name
    * @param value
    */
   public static void setContextAttribute(HttpServletRequest request, String name, Object value) {
      JAssert.checkNotNull(request, "Request must not be null");
      if (value != null) {
         request.getSession().getServletContext().setAttribute(name, value);
      } else {
         HttpSession session = request.getSession(false);
         if (session != null) {
            session.getServletContext().removeAttribute(name);
         }
      }
   }

   /**
    * 获得或创建{@link ServletContext}属性
    * 
    * @param context
    * @param name
    * @param clazz
    * @return
    * @throws IllegalArgumentException
    */
   public static Object getOrCreateContextAttribute(JActionContext context, String name, Class<?> clazz)
         throws IllegalArgumentException {
      JAssert.checkNotNull(context, "context must not be null");
      return getOrCreateContextAttribute(context.getRequest(), name, clazz);
   }

   /**
    * 获得或创建{@link ServletContext}属性
    * 
    * @param request
    * @param name
    * @param clazz
    * @return
    * @throws IllegalArgumentException
    */
   public static Object getOrCreateContextAttribute(HttpServletRequest request, String name, Class<?> clazz)
         throws IllegalArgumentException {

      JAssert.checkNotNull(request, "request must not be null");
      ServletContext context = request.getSession().getServletContext();
      Object contextObject = context.getAttribute(name);
      if (contextObject == null) {
         try {
            contextObject = clazz.newInstance();
         } catch (InstantiationException ex) {
            throw new IllegalArgumentException("Could not instantiate class [" + clazz.getName()
                  + "] for application attribute '" + name + "': " + ex.getMessage());
         } catch (IllegalAccessException ex) {
            throw new IllegalArgumentException("Could not access default constructor of class [" + clazz.getName()
                  + "] for application attribute '" + name + "': " + ex.getMessage());
         }
         context.setAttribute(name, contextObject);
      }
      return contextObject;
   }

   public static boolean isIncludeRequest(ServletRequest request) {
      return (request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE) != null);
   }

   /**
    * 获得{@link Cookie}对象
    * 
    * @param request
    * @param name
    * @return
    */
   public static Cookie getCookie(HttpServletRequest request, String name) {
      JAssert.checkNotNull(request, "Request must not be null");
      Cookie cookies[] = request.getCookies();
      if (cookies != null) {
         for (Cookie cookie : cookies) {
            if (name.equals(cookie.getName())) {
               return cookie;
            }
         }
      }
      return null;
   }

   /**
    * 获得已指定前缀开始的请求参数
    * 
    * @param request
    * @param prefix
    * @return
    */
   public static Map<String, Object> getParametersStartingWith(ServletRequest request, String prefix) {
      JAssert.checkNotNull(request, "Request must not be null");
      @SuppressWarnings("unchecked")
      Enumeration<String> paramNames = request.getParameterNames();
      Map<String, Object> params = new TreeMap<String, Object>();
      if (prefix == null) {
         prefix = "";
      }
      while (paramNames != null && paramNames.hasMoreElements()) {
         String paramName = paramNames.nextElement();
         if ("".equals(prefix) || paramName.startsWith(prefix)) {
            String unprefixed = paramName.substring(prefix.length());
            String[] values = request.getParameterValues(paramName);
            if (values == null || values.length == 0) {
               // Do nothing, no values found at all.
            } else if (values.length > 1) {
               params.put(unprefixed, values);
            } else {
               params.put(unprefixed, values[0]);
            }
         }
      }
      return params;
   }

   /**
    * 判断是否为multipart内容
    * 
    * @param request
    * @return
    */
   public static boolean isMultipartContent(HttpServletRequest request) {
      if (!"post".equals(request.getMethod().toLowerCase())) {
         return false;
      } else {
         String contentType = request.getContentType();
         return contentType == null ? false : contentType.toLowerCase().startsWith("multipart/");
      }
   }

   /**
    * 获取对象中的第一个字符串.
    */
   public static String getFirstParam(Object obj) {
      if (obj == null) {
         return null;
      }
      if (obj instanceof String[]) {
         String[] arr = (String[]) obj;
         return arr.length > 0 ? arr[0] : null;
      }
      if (obj instanceof String) {
         return (String) obj;
      }
      if (obj instanceof Object[]) {
         Object[] arr = (Object[]) obj;
         return arr.length > 0 ? getFirstParam(arr[0]) : null;
      }
      if (obj instanceof Collection) {
         Collection<?> c = (Collection<?>) obj;
         return c.size() > 0 ? getFirstParam(c.iterator().next()) : null;
      }
      return obj.toString();
   }

   /**
    * 对象转换为字符串数组.
    */
   public static String[] getParams(Object obj) {
      if (obj == null) {
         return null;
      }
      if (obj instanceof String[]) {
         return (String[]) obj;
      }
      if (obj instanceof Object[]) {
         Object[] arr = (Object[]) obj;
         int len = arr.length;
         String[] tmp = new String[len];
         for (int i = 0; i < len; i++) {
            tmp[i] = JStringUtils.toString(arr[i]);
         }
         return tmp;
      }
      if (obj instanceof Collection) {
         Collection<?> c = (Collection<?>) obj;
         int len = c.size(), index = 0;
         String[] tmp = new String[len];
         Iterator<?> iterator = c.iterator();

         while (iterator.hasNext()) {
            tmp[index++] = JStringUtils.toString(iterator.next());
         }
         return tmp;
      }
      return new String[] { obj.toString() };
   }

   /**
    * 获得客户端IP
    * 
    * @param request
    * @return
    */
   public static String getRemoteAddress(HttpServletRequest request) {
      String ip = request.getHeader("x-forwarded-for");
      if (JStringUtils.isBlank(ip) || ip.equalsIgnoreCase("unknown")) {
         ip = request.getHeader("Proxy-Client-IP");
      }
      if (JStringUtils.isBlank(ip) || ip.equalsIgnoreCase("unknown")) {
         ip = request.getHeader("WL-Proxy-Client-IP");
      }
      if (JStringUtils.isBlank(ip) || ip.equalsIgnoreCase("unknown")) {
         ip = request.getRemoteAddr();
      }
      return ip;
   }

   /**
    * 获得主机地址
    * 
    * @param request
    * @return
    */
   public static String getHost(HttpServletRequest request) {
      StringBuilder url = new StringBuilder();
      url.append(request.getScheme()).append("://").append(request.getServerName());
      if (request.getServerPort() != 80) {
         url.append(":").append(request.getServerPort());
      }
      String cp = request.getContextPath();
      if (cp.equals("/")) {
         cp = "";
      }
      url.append(cp);
      return url.toString();
   }

   public static void doSetCookie(HttpServletResponse response, String name, String value, int maxAgeInSeconds,
         String path, String domain, Boolean isHttpOnly) {
      Cookie cookie = new Cookie(name, value);
      cookie.setMaxAge(maxAgeInSeconds);
      // set the default path value to "/"
      if (path == null) {
         path = "/";
      }
      cookie.setPath(path);

      if (domain != null) {
         cookie.setDomain(domain);
      }
      doSetCookie(response, cookie, isHttpOnly);
   }

   /**
    * JAVA EE5中Cookie没有处理isHttpOnly，为了兼容，重写相关方法，直接添加响应头
    * @param response
    * @param cookie
    * @param isHttpOnly
    */
   public static void doSetCookie(HttpServletResponse response, Cookie cookie, Boolean isHttpOnly) {

      StringBuilder header = new StringBuilder();
      // TODO: Name validation takes place in Cookie and can not be configured
      // per Context. Moving it to here would allow per Context config
      // but delay validation until the header is generated. However,
      // the spec requires an IllegalArgumentException on Cookie
      // generation.
      header.append(cookie.getName());
      header.append('=');
      String value = cookie.getValue();
      if (value != null && value.length() > 0) {
         validateCookieValue(value);
         header.append(value);
      }

      // RFC 6265 prefers Max-Age to Expires so use Max-Age
      int maxAge = cookie.getMaxAge();
      if (maxAge > -1) {
         // Negative Max-Age is equivalent to no Max-Age
         header.append(";Max-Age=");
         header.append(maxAge);
      }

      String domain = cookie.getDomain();
      if (domain != null && domain.length() > 0) {
         validateDomain(domain);
         header.append(";domain=");
         header.append(domain);
      }

      String path = cookie.getPath();
      if (path != null && path.length() > 0) {
         validatePath(path);
         header.append(";path=");
         header.append(path);
      }

      if (cookie.getSecure()) {
         header.append(";Secure");
      }

      if (isHttpOnly != null && isHttpOnly) {
         header.append(";HttpOnly");
      }
      response.addHeader("Set-Cookie", header.toString());
   }

   private static void validateCookieValue(String value) {
      int start = 0;
      int end = value.length();

      if (end > 1 && value.charAt(0) == '"' && value.charAt(end - 1) == '"') {
         start = 1;
         end--;
      }

      char[] chars = value.toCharArray();
      for (int i = start; i < end; i++) {
         char c = chars[i];
         if (c < 0x21 || c == 0x22 || c == 0x2c || c == 0x3b || c == 0x5c || c == 0x7f) {
            throw new IllegalArgumentException("无效的value值：" + Integer.toString(c));
         }
      }
   }

   private static void validateDomain(String domain) {
      int i = 0;
      int prev = -1;
      int cur = -1;
      char[] chars = domain.toCharArray();
      while (i < chars.length) {
         prev = cur;
         cur = chars[i];
         if (!domainValid.get(cur)) {
            throw new IllegalArgumentException("无效的domain值：" + domain);
         }
         // labels must start with a letter or number
         if ((prev == '.' || prev == -1) && (cur == '.' || cur == '-')) {
            throw new IllegalArgumentException("无效的domain值：" + domain);
         }
         // labels must end with a letter or number
         if (prev == '-' && cur == '.') {
            throw new IllegalArgumentException("无效的domain值：" + domain);
         }
         i++;
      }
      // domain must end with a label
      if (cur == '.' || cur == '-') {
         throw new IllegalArgumentException("无效的domain值：" + domain);
      }
   }

   private static void validatePath(String path) {
      char[] chars = path.toCharArray();

      for (int i = 0; i < chars.length; i++) {
         char ch = chars[i];
         if (ch < 0x20 || ch > 0x7E || ch == ';') {
            throw new IllegalArgumentException("无效的path值：" + path);
         }
      }
   }

   /**
    * 从Content-Type头中解析字符集，如果该字符集不被支持则返回null
    * 
    * @param contentType
    *           示例 "text/html; charset=EUC-JP"
    * @return "EUC-JP", or null if not found. Charset is trimmed and uppercased.
    */
   public static String getCharsetFromContentType(String contentType) {
      if (contentType == null) {
         return null;
      }
      Matcher m = charsetPattern.matcher(contentType);
      if (m.find()) {
         String charset = m.group(1).trim();
         charset = charset.replace("charset=", "");
         return validateCharset(charset);
      }
      return null;
   }

   /**
    * 获得请求字编码
    * 
    * @param request
    * @return
    * @throws IOException
    */
   public static String getRequestEncoding(HttpServletRequest request) throws IOException {
      String encoding = request.getCharacterEncoding();
      if (JStringUtils.isBlank(encoding)) {
         encoding = getCharsetFromContentType(request.getContentType());
         request.setCharacterEncoding(encoding);
      }
      if (JStringUtils.isBlank(encoding)) {
         encoding = DEFAULT_CHARACTER_ENCODING;
         request.setCharacterEncoding(encoding);
      }
      return encoding;
   }

   /**
    * 获得响应编码
    * 
    * @param response
    * @return
    * @throws IOException
    */
   public static String getResponseEncoding(HttpServletResponse response) throws IOException {
      String encoding = response.getCharacterEncoding();
      if (JStringUtils.isBlank(encoding)) {
         encoding = DEFAULT_CHARACTER_ENCODING;
         response.setCharacterEncoding(encoding);
      }
      return encoding;
   }

   /**
    * 验证字符集
    * 
    * @param cs
    * @return
    */
   public static String validateCharset(String cs) {
      if (cs == null || cs.length() == 0) {
         return null;
      }
      cs = cs.trim().replaceAll("[\"']", "");
      try {
         if (Charset.isSupported(cs)) {
            return cs;
         }
         cs = cs.toUpperCase(Locale.ENGLISH);
         if (Charset.isSupported(cs)) {
            return cs;
         }
      } catch (IllegalCharsetNameException e) {
      }
      return null;
   }

   /**
    * 获得浏览器名称
    * 
    * @param request
    * @return
    */
   public static String getBrowserName(HttpServletRequest request) {
      String agent = request.getHeader("User-Agent").toLowerCase();
      if (JStringUtils.isBlank(agent)) {
         return "Others";
      }
      if (agent.indexOf("msie 7") > 0) {
         return "ie7";
      }
      if (agent.indexOf("msie 8") > 0) {
         return "ie8";
      }
      if (agent.indexOf("msie 9") > 0) {
         return "ie9";
      }
      if (agent.indexOf("msie 10") > 0) {
         return "ie10";
      }
      if (agent.indexOf("msie") > 0) {
         return "ie";
      }
      if (agent.indexOf("opera") > 0) {
         return "opera";
      }
      if (agent.indexOf("firefox") > 0) {
         return "firefox";
      }
      if (agent.indexOf("webkit") > 0) {
         return "webkit";
      }
      if (agent.indexOf("gecko") > 0 && agent.indexOf("rv:11") > 0) {
         return "ie11";
      } else {
         return "Others";
      }
   }
}
